// Copyright (c) 2014 Liyong Zou and Int Li
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
// OR OTHER DEALINGS IN THE SOFTWARE.



#ifndef FUNCTION_HPP
#define FUNCTION_HPP

#include "from_lua.hpp"
#include "to_lua.hpp"


extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

#include <string>
#include <typeinfo>
#include <memory>
#include <sstream>
// #include <iostream>

#ifdef USE_BOOST
#include <boost/function.hpp>
using boost::function;
#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
#else
#include <functional>
using std::function;
using std::shared_ptr;
#endif

namespace luaglue {

/*
#define def_a_ret_val_template(TRET, TARGLIST...) \
    void to_lua(lua_State *L, TRET (*f)(TARGLIST)) \
{ \
    lua_pushlightuserdata(L, (void *)f); \
    lua_pushcclosure(L, &(to_lua_func_wrapper<TRET, ##TARGLIST>), 1); \
}
#define def_no_ret_val_template(TARGLIST...) \
    void to_lua(lua_State *L, void (*f)(TARGLIST)) \
{ \
    lua_pushlightuserdata(L, (void *)f); \
    lua_pushcclosure(L, &(to_lua_func_wrapper_noret<TARGLIST>), 1); \
}
*/

template<class TRET, class ... TARGS>
inline function<TRET (TARGS...)>* __get_actual_func(lua_State *L)
{
    return static_cast<function<TRET (TARGS...)>*>(lua_touserdata(L, lua_upvalueindex(1)));
}

// no arg
template<class TRET>
int to_lua_func_wrapper(lua_State *L)
{
    to_lua(L, (*__get_actual_func<TRET>(L))());

    return 1;
}

template<int A = 0>
int to_lua_func_wrapper_noret(lua_State *L)
{
    (*__get_actual_func<void>(L))();

    return 0;
}

// 1 arg
template<class TRET, class T_1>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1>(L))(*arg1));

    return 1;
}

template<class T_1>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);

    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    (*__get_actual_func<void, T_1>(L))(*arg1);

    return 0;
}
////****************Int Li*****************////

// 2 arg
template<class TRET, class T_1, class T_2>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1, T_2>(L))\
           (*arg1, *arg2));

    return 1;
}

template<class T_1, class T_2>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    (*__get_actual_func<void,T_1, T_2>(L))\
            (*arg1, *arg2);

    return 0;
}

// 3 arg
template<class TRET, class T_1, class T_2,  class T_3>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1, T_2, T_3>(L))\
           (*arg1, *arg2, *arg3));

    return 1;
}

template<class T_1, class T_2, class T_3>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expecpted").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    (*__get_actual_func<void, T_1, T_2, T_3>(L))\
            (*arg1, *arg2, *arg3);

    return 0;
}

// 4 arg
template<class TRET,class T_1, class T_2, class T_3, class T_4>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1, T_2, T_3, T_4>(L))\
           (*arg1, *arg2, *arg3, *arg4));

    return 1;
}

template<class T_1, class T_2, class T_3, class T_4>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expecpted").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    (*__get_actual_func<void, T_1, T_2, T_3, T_4>(L))\
            (*arg1, *arg2, *arg3, *arg4);

    return 0;
}

// 5 arg
template<class TRET, class T_1, class T_2, class T_3, class T_4, class T_5>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1, T_2, T_3, T_4, T_5>(L))\
           (*arg1, *arg2, *arg3, *arg4, *arg5));

    return 1;
}

template<class T_1, class T_2, class T_3, class T_4, class T_5>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expecpted").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    (*__get_actual_func<void, T_1, T_2, T_3, T_4, T_5>(L))\
            (*arg1, *arg2, *arg3, *arg4, *arg5);

    return 0;
}

// 6 arg
template<class TRET, class T_1,class T_2, class T_3, class T_4, class T_5, class T_6>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1, T_2, T_3, T_4, T_5, T_6>(L))\
           (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6));

    return 1;
}

template<class T_1,class T_2, class T_3, class T_4, class T_5, class T_6>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expecpted").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    (*__get_actual_func<void, T_1, T_2, T_3, T_4, T_5, T_6>(L))\
            (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6);

    return 0;
}

// 7 arg
template<class TRET, class T_1, class T_2, class T_3, class T_4, class T_5, class T_6, class T_7>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    holder<T_7> arg7 = from_lua<T_7>()(L, 7);
    luaL_argcheck(L, arg7, 7, (std::string(typeid(T_7).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1, T_2, T_3, T_4, T_5, T_6, T_7>(L))\
           (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7));

    return 1;
}

template<class T_1, class T_2, class T_3, class T_4, class T_5, class T_6, class T_7>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expecpted").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    holder<T_7> arg7 = from_lua<T_7>()(L, 7);
    luaL_argcheck(L, arg7, 7, (std::string(typeid(T_7).name()) + " expcepted!").c_str());
    (*__get_actual_func<void, T_1, T_2, T_3, T_4, T_5, T_6, T_7>(L))\
            (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7);

    return 0;
}

// 8 arg
template<class TRET, class T_1, class T_2, class T_3, class T_4, class T_5, class T_6, class T_7, class T_8>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    holder<T_7> arg7 = from_lua<T_7>()(L, 7);
    luaL_argcheck(L, arg7, 7, (std::string(typeid(T_7).name()) + " expcepted!").c_str());
    holder<T_8> arg8 = from_lua<T_8>()(L, 8);
    luaL_argcheck(L, arg8, 8, (std::string(typeid(T_8).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8>(L))\
           (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8));

    return 1;
}
template<class T_1, class T_2, class T_3, class T_4, class T_5, class T_6, class T_7, class T_8>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expecpted").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    holder<T_7> arg7 = from_lua<T_7>()(L, 7);
    luaL_argcheck(L, arg7, 7, (std::string(typeid(T_7).name()) + " expcepted!").c_str());
    holder<T_8> arg8 = from_lua<T_8>()(L, 8);
    luaL_argcheck(L, arg8, 8, (std::string(typeid(T_8).name()) + " expcepted!").c_str());
    (*__get_actual_func<void, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8>(L))\
            (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8);

    return 0;
}

// 9 arg
template<class TRET, class T_1, class T_2, class T_3, class T_4, class T_5, class T_6, class T_7, class T_8, class T_9>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    holder<T_7> arg7 = from_lua<T_7>()(L, 7);
    luaL_argcheck(L, arg7, 7, (std::string(typeid(T_7).name()) + " expcepted!").c_str());
    holder<T_8> arg8 = from_lua<T_8>()(L, 8);
    luaL_argcheck(L, arg8, 8, (std::string(typeid(T_8).name()) + " expcepted!").c_str());
    holder<T_9> arg9 = from_lua<T_9>()(L, 9);
    luaL_argcheck(L, arg9, 9, (std::string(typeid(T_9).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9>(L))\
           (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9));

    return 1;
}

template<class T_1, class T_2, class T_3, class T_4, class T_5, class T_6, class T_7, class T_8, class T_9>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expecpted").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    holder<T_7> arg7 = from_lua<T_7>()(L, 7);
    luaL_argcheck(L, arg7, 7, (std::string(typeid(T_7).name()) + " expcepted!").c_str());
    holder<T_8> arg8 = from_lua<T_8>()(L, 8);
    luaL_argcheck(L, arg8, 8, (std::string(typeid(T_8).name()) + " expcepted!").c_str());
    holder<T_9> arg9 = from_lua<T_9>()(L, 9);
    luaL_argcheck(L, arg9, 9, (std::string(typeid(T_9).name()) + " expcepted!").c_str());
    (*__get_actual_func<void, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9>(L))\
            (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9);

    return 0;
}

// 10 arg
template<class TRET, class T_1, class T_2, class T_3, class T_4, class T_5, class T_6, class T_7, class T_8,class T_9, class T_10>
int to_lua_func_wrapper(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expcepted!").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    holder<T_7> arg7 = from_lua<T_7>()(L, 7);
    luaL_argcheck(L, arg7, 7, (std::string(typeid(T_7).name()) + " expcepted!").c_str());
    holder<T_8> arg8 = from_lua<T_8>()(L, 8);
    luaL_argcheck(L, arg8, 8, (std::string(typeid(T_8).name()) + " expcepted!").c_str());
    holder<T_9> arg9 = from_lua<T_9>()(L, 9);
    luaL_argcheck(L, arg9, 9, (std::string(typeid(T_8).name()) + " expcepted!").c_str());
    holder<T_10> arg10 = from_lua<T_10>()(L, 10);
    luaL_argcheck(L, arg10, 10, (std::string(typeid(T_10).name()) + " expcepted!").c_str());
    to_lua(L, (*__get_actual_func<TRET, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9, T_10>(L))\
           (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9, *arg10));

    return 1;
}

template<class T_1, class T_2, class T_3, class T_4, class T_5, class T_6, class T_7, class T_8, class T_9, class T_10>
int to_lua_func_wrapper_noret(lua_State *L)
{
    holder<T_1> arg1 = from_lua<T_1>()(L, 1);
    luaL_argcheck(L, arg1, 1, (std::string(typeid(T_1).name()) + " expcepted!").c_str());
    holder<T_2> arg2 = from_lua<T_2>()(L, 2);
    luaL_argcheck(L, arg2, 2, (std::string(typeid(T_2).name()) + " expecpted").c_str());
    holder<T_3> arg3 = from_lua<T_3>()(L, 3);
    luaL_argcheck(L, arg3, 3, (std::string(typeid(T_3).name()) + " expcepted!").c_str());
    holder<T_4> arg4 = from_lua<T_4>()(L, 4);
    luaL_argcheck(L, arg4, 4, (std::string(typeid(T_4).name()) + " expcepted!").c_str());
    holder<T_5> arg5 = from_lua<T_5>()(L, 5);
    luaL_argcheck(L, arg5, 5, (std::string(typeid(T_5).name()) + " expcepted!").c_str());
    holder<T_6> arg6 = from_lua<T_6>()(L, 6);
    luaL_argcheck(L, arg6, 6, (std::string(typeid(T_6).name()) + " expcepted!").c_str());
    holder<T_7> arg7 = from_lua<T_7>()(L, 7);
    luaL_argcheck(L, arg7, 7, (std::string(typeid(T_7).name()) + " expcepted!").c_str());
    holder<T_8> arg8 = from_lua<T_8>()(L, 8);
    luaL_argcheck(L, arg8, 8, (std::string(typeid(T_8).name()) + " expcepted!").c_str());
    holder<T_9> arg9 = from_lua<T_9>()(L, 9);
    luaL_argcheck(L, arg9, 9, (std::string(typeid(T_9).name()) + " expcepted!").c_str());
    holder<T_10> arg10 = from_lua<T_10>()(L, 10);
    luaL_argcheck(L, arg10, 10, (std::string(typeid(T_10).name()) + " expcepted!").c_str());
    (*__get_actual_func<void, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9, T_10>(L))\
            (*arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9, *arg10);

    return 0;
}
////**************************************////

template<class TRET, class ... TARGS>
inline void to_lua(lua_State *L, function<TRET (TARGS...)>f)
{
    new(lua_newuserdata(L, sizeof(f)))function<TRET (TARGS...)>(f);
    lua_pushcclosure(L, &(to_lua_func_wrapper<TRET, TARGS...>), 1);
}

template<class ... TARGS>
inline void to_lua(lua_State *L, function<void (TARGS...)>f)
{
    new(lua_newuserdata(L, sizeof(f)))function<void (TARGS...)>(f);
    lua_pushcclosure(L, &(to_lua_func_wrapper_noret<TARGS ...>), 1);
}

template <class TRET, class ... TARGS>
inline void to_lua(lua_State *L, TRET(*func)(TARGS ...))
{
    function<TRET(TARGS...)> _func = func;
    to_lua(L, _func);
}

template<>
inline void to_lua(lua_State *L, int (*func)(lua_State *))
{
    lua_pushcfunction(L, func);
}

template <class TRET, class ... TARGS>
inline void def(lua_State *L, const std::string &fn, TRET(*func)(TARGS ...))
{
    function<TRET(TARGS...)> _func = func;
    to_lua(L, _func);
    lua_setglobal(L, fn.c_str());
}

}

#endif // FUNCTION_HPP

